package com.wxd.woff.table.glyf;

import com.wxd.woff.AbstractOTFDataStream;
import com.wxd.woff.AbstractWoffTable;
import com.wxd.woff.ByteBufferOTFDataStream;
import com.wxd.woff.WoffFont;
import com.wxd.woff.table.HorizontalMetricsTable;
import com.wxd.woff.table.IndexToLocationTable;

import java.io.IOException;
import java.util.Arrays;

/**
 * glyf table
 * Created with IntelliJ IDEA.
 *
 * @author: wangxindong
 * @date: 2018/9/2-09:37
 */
public class GlyphTable extends AbstractWoffTable {

    public static final String TAG = "glyf";

    private IndexToLocationTable loca;
    private int numGlyphs;
    private AbstractOTFDataStream data;
    private GlyphData[] glyphs;
    private int cached = 0;

    private WoffFont font;

    /**
     * Don't even bother to cache huge fonts.
     */
    private static final int MAX_CACHE_SIZE = 5000;

    /**
     * Don't cache more glyphs than this.
     */
    private static final int MAX_CACHED_GLYPHS = 100;

    public GlyphTable(WoffFont woffFont) {
        super(woffFont);
    }

    @Override
    public void read(WoffFont woffFont, AbstractOTFDataStream stream) throws IOException {
        loca = woffFont.getIndexToLocation();
        numGlyphs = woffFont.getNumberOfGlyphs();

        stream.seek(offset);
        byte[] fontData = stream.read(compLength);
        byte[] inflateFont = inflateFontData(fontData);
        AbstractOTFDataStream data = new ByteBufferOTFDataStream(inflateFont);
        // cache the stream
        this.data = data;
        this.font = woffFont;

        if (numGlyphs < MAX_CACHE_SIZE) {
            glyphs = new GlyphData[numGlyphs];
        }
        getGlyphs();
    }

    /**
     * 获取所有的Glyph表
     */
    public GlyphData[] getGlyphs() throws IOException {
        synchronized (font) {
            // the glyph offsets
            long[] offsets = loca.getOffsets();
            long endOfGlyphs = offsets[numGlyphs];
            if (glyphs == null) {
                glyphs = new GlyphData[numGlyphs];
            }
            for (int gid = 0; gid < numGlyphs; gid++) {
                // end of glyphs reached?
                if (endOfGlyphs != 0 && endOfGlyphs == offsets[gid]) {
                    break;
                }
                if (offsets[gid + 1] <= offsets[gid]) {
                    continue;
                }
                if (glyphs[gid] != null) {
                    // already cached
                    continue;
                }
                data.seek(offsets[gid]);
                if (glyphs[gid] == null) {
                    ++cached;
                }
                glyphs[gid] = getGlyphData(gid);
            }
            return glyphs;
        }
    }


    public GlyphData getGlyph(int gid) throws IOException {
        if (gid < 0 || gid >= numGlyphs) {
            return null;
        }
        if (glyphs != null && glyphs[gid] != null) {
            return glyphs[gid];
        }
        synchronized (font) {
            // read a single glyph
            long[] offsets = loca.getOffsets();
            if (offsets[gid] == offsets[gid + 1]) {
                // no outline
                return null;
            }
            // save
            long currentPosition = data.getCurrentPosition();
            data.seek(offsets[gid]);
            GlyphData glyph = getGlyphData(gid);
            // restore
            data.seek(currentPosition);
            if (glyphs != null && glyphs[gid] == null && cached < MAX_CACHED_GLYPHS) {
                glyphs[gid] = glyph;
                ++cached;
            }
            return glyph;
        }
    }

    private GlyphData getGlyphData(int gid) throws IOException {
        GlyphData glyph = new GlyphData();
        HorizontalMetricsTable hmt = font.getHorizontalMetrics();
        int leftSideBearing = hmt == null ? 0 : hmt.getLeftSideBearing(gid);
        glyph.initData(this, data, leftSideBearing);
        // resolve composite glyph
        if (glyph.getDescription().isComposite()) {
            glyph.getDescription().resolve();
        }
        return glyph;
    }

    public IndexToLocationTable getLoca() {
        return loca;
    }

    public void setLoca(IndexToLocationTable loca) {
        this.loca = loca;
    }

    public int getNumGlyphs() {
        return numGlyphs;
    }

    public void setNumGlyphs(int numGlyphs) {
        this.numGlyphs = numGlyphs;
    }

    public AbstractOTFDataStream getData() {
        return data;
    }

    public void setData(AbstractOTFDataStream data) {
        this.data = data;
    }

    public void setGlyphs(GlyphData[] glyphs) {
        this.glyphs = glyphs;
    }

    public int getCached() {
        return cached;
    }

    public void setCached(int cached) {
        this.cached = cached;
    }

    public WoffFont getFont() {
        return font;
    }

    public void setFont(WoffFont font) {
        this.font = font;
    }

    @Override
    public String toString() {
        return "AbstractWoffTable{" +
                "tag='" + tag + '\'' +
                ", offset=" + offset +
                ", compLength=" + compLength +
                ", origLength=" + origLength +
                ", origChecksum=" + origChecksum +
                ", loca=" + loca +
                ", numGlyphs=" + numGlyphs +
                ", glyphs=" + Arrays.toString(glyphs) +
                '}';
    }
}
